﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Axe.Collections
{
    /// <summary>
    /// Класс с протоколированием доступа к элементам
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class TimeStampDictionary<TKey,TValue> : IDictionary<TKey,TValue>
    {
        Dictionary<TKey, TValue> internalDictionary = new Dictionary<TKey, TValue>();
        SyncDictionary<TKey, DateTime> accessTimestamps = new SyncDictionary<TKey, DateTime>();

        #region IDictionary<TKey,TValue> Members

        public void Add(TKey key, TValue value)
        {
            internalDictionary.Add(key, value);
            accessTimestamps.Add(key, DateTime.UtcNow);
        }

        public bool ContainsKey(TKey key)
        {
            bool containsKey = internalDictionary.ContainsKey(key);

            if (containsKey)
            {
                accessTimestamps[key] = DateTime.UtcNow;
            }

            return containsKey;
        }

        public ICollection<TKey> Keys
        {
            get { return Keys; }
        }

        public bool Remove(TKey key)
        {
            bool removed = internalDictionary.Remove(key);
            if (removed)
                accessTimestamps.Remove(key);
            return removed;
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            bool got = internalDictionary.TryGetValue(key, out value);
            if (got)
                accessTimestamps[key] = DateTime.UtcNow;
            return got;
        }

        public ICollection<TValue> Values
        {
            get { return Values; }
        }

        public TValue this[TKey key]
        {
            get
            {
                TValue value = internalDictionary[key];
                accessTimestamps[key] = DateTime.UtcNow;
                return value;
            }
            set
            {
                internalDictionary[key] = value;
                accessTimestamps[key] = DateTime.UtcNow;
            }
        }

        #endregion

        #region ICollection<KeyValuePair<TKey,TValue>> Members

        void ICollection<KeyValuePair<TKey,TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            Add(item.Key, item.Value);
        }

        public void Clear()
        {
            internalDictionary.Clear();
            accessTimestamps.Clear();
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
        {
            return ContainsKey(item.Key);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            (internalDictionary as ICollection<KeyValuePair<TKey, TValue>>).CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return internalDictionary.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            return Remove(item.Key);
        }

        #endregion

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return internalDictionary.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return internalDictionary.GetEnumerator();
        }

        #endregion

        /// <summary>
        /// Удаляет из коллекции все элементы, которые "старше" указанного возраста
        /// </summary>
        /// <param name="span"></param>
        public void RemoveOlder(TimeSpan span)
        {
            List<TKey> keysToRemove = new List<TKey>();
            DateTime minTime = DateTime.UtcNow - span;

            foreach (KeyValuePair<TKey, DateTime> stamp in accessTimestamps)
            {
                if (stamp.Value < minTime)
                {
                    keysToRemove.Add(stamp.Key);
                }
            }

            keysToRemove.ForEach(delegate(TKey key) 
            {
                internalDictionary.Remove(key);
                accessTimestamps.Remove(key);
            });
        }
    }
}
